package com.knowledgePointsByMyTest.JavaNIO;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

/**
 * @author yulongtian
 * @create 2024-11-04 14:53
 */
public class NIOClient {
    public static void main(String[] args) throws IOException {
        // 1 窗口客户端socketchannel 绑定客户端本地地址（不选 默认随机分配一个可用地址）
        SocketChannel socketChannel = SocketChannel.open();
        // 2 设置非阻塞模式
        socketChannel.configureBlocking(false);
        // 3 创建selector
        Selector selector = Selector.open();
        // 4 创建Reactor线程
        new Thread(new SingleClient(socketChannel, selector)).start();
    }
}

class SingleClient implements Runnable {

    private final SocketChannel socketChannel;
    private final Selector selector;

    public SingleClient(SocketChannel socketChannel, Selector selector) {
        this.socketChannel = socketChannel;
        this.selector = selector;
    }

    @Override
    public void run() {
        try {
            // 连接服务器
            doConnect(socketChannel, selector);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }

        // 5 多路复用器执行多路复用程序
        while (true) {
            try {
                selector.select(1000);
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    processKey(selectionKey);
                    iterator.remove();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    private void processKey(SelectionKey key) throws IOException {
        if (key.isValid()) {
            // 按照事件类型进行区分
            if (key.isReadable()) {
                //有数据过来
                SocketChannel socketChannel = (SocketChannel) key.channel();
                ByteBuffer allocate = ByteBuffer.allocate(1024);
                socketChannel.read(allocate);

                //应用程序从buffer中拿到数据
                allocate.flip();

                byte[] bytes = new byte[allocate.remaining()];
                allocate.get(bytes);
                String msg = new String(bytes, Charset.defaultCharset());
                System.out.println("收到了来自客户端的数据:" + msg);

                // 业务操作


                // 完成业务操作，向客户端写点数据
                allocate.clear();
                allocate.put("hello nio server, i am nio client".getBytes(StandardCharsets.UTF_8));
                allocate.flip();
                socketChannel.write(allocate);
            }
        }
    }

    private void doConnect(SocketChannel socketChannel, Selector selector) throws IOException {
        System.out.println("客户端成功启动，开始连接服务器");
        // 3 连接服务器
        boolean connect = socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888));
        System.out.println("connect success:" + connect);
        // 将socketChannel注册到selector并判断是否连接成功，连接成功监听读事件，没有，则继续监听连接事件
        if (connect) {
            socketChannel.register(selector, SelectionKey.OP_READ);
            System.out.println("客户端连接成功服务端，准备发送数据");

        }
    }
}